Skip to content

feat(ui): add screenshot button to action bar (#1394)#1434

Open
nitishagar wants to merge 1 commit into
jetkvm:devfrom
nitishagar:feat/screenshot-button
Open

feat(ui): add screenshot button to action bar (#1394)#1434
nitishagar wants to merge 1 commit into
jetkvm:devfrom
nitishagar:feat/screenshot-button

Conversation

@nitishagar
Copy link
Copy Markdown
Contributor

@nitishagar nitishagar commented Apr 30, 2026

Closes #1394.

Adds a "Screenshot" button next to the Fullscreen button on the action bar. Captures the current WebRTC <video> frame via an offscreen canvas and downloads a PNG at the native stream resolution.

Changes

  • ui/src/components/WebRTCVideo.tsx — new takeScreenshot callback, wired to the existing videoElm ref; guards against unready streams (videoWidth/videoHeight === 0).
  • ui/src/components/ActionBar.tsx — new LuCamera button inside the existing lg:flex wrapper, positioned immediately before the Fullscreen SplitButtonGroup.
  • ui/localization/messages/*.json — new action_bar_screenshot key, machine-translated to every existing locale.

Implementation notes

  • Filename format: JetKVM <W>x<H> <YYYY-MM-DD> at <HH.MM.SS>.png, matching the reporter's proposal. Timestamp is built from local Date components (not toISOString()) so that a 6pm-local screenshot actually reads 18.00.00 instead of a UTC-shifted time that looks like a bug.
  • Icon is LuCamera from react-icons/lu — no new dependency, visually consistent with the sibling icons (LuMaximize, LuSettings, etc.).
  • The callback lives in WebRTCVideo.tsx and is passed down as a prop, mirroring the existing requestFullscreen plumbing through the same component interface.
  • Button is gated behind the same lg:flex breakpoint wrapper as Fullscreen. Happy to surface it on smaller breakpoints if preferred.
  • No CORS taint concern: the <video> is fed by a local WebRTC MediaStream, so canvas.toDataURL is safe.

Testing

  • npm run lint:only, npm run build:device, npm run i18n:validate, and npm run i18n:find-unused all pass locally.
  • End-to-end device verification is pending — I don't currently have JetKVM hardware on hand. Reviewer verification on a live device would be appreciated.

Note

Low Risk
Low risk UI-only change that captures a client-side video frame to a downloaded PNG; main risk is minor UX/browser compatibility around canvas capture and download behavior.

Overview
Adds a new Screenshot control to the KVM Actionbar, wired from WebRTCVideo via a new takeScreenshot prop.

The screenshot implementation captures the current WebRTC <video> frame into an offscreen canvas and triggers a PNG download with a timestamped filename, with guards for uninitialized video dimensions.

Updates i18n message catalogs to include the new action_bar_screenshot label across locales (and fills in additional missing Welsh strings related to advanced reset/network time sync).

Reviewed by Cursor Bugbot for commit f4b54f5. Bugbot is set up for automated code reviews on this repo. Configure here.

Adds a Screenshot button next to the existing Fullscreen button that
captures the current WebRTC video frame to a PNG download at the native
stream resolution. Filename format:
'JetKVM <W>x<H> <YYYY-MM-DD> at <HH.MM.SS>.png'.

Closes jetkvm#1394
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f4b54f5. Configure here.

document.body.appendChild(a);
a.click();
a.remove();
}, []);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot ignores user video display filter settings

Medium Severity

The takeScreenshot callback draws the raw video frame via ctx.drawImage but does not apply the user's videoSaturation, videoBrightness, or videoContrast CSS filters that are actively applied to the <video> element through videoStyle. When a user has adjusted these display settings, the downloaded screenshot will look different from what they see on screen. The canvas 2D context's filter property can replicate the same CSS filter string before drawing. The useCallback dependency array [] would also need to include those settings values.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f4b54f5. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Screenshot button

1 participant